// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
#pragma once
#include "ForecastGroup.h"
#include "IWeatherService.h"

namespace Weathr
{
    [Windows::Foundation::Metadata::WebHostHidden]
    public delegate void ForecastAddedHandler(Forecast^ forecast);

    [Windows::UI::Xaml::Data::Bindable]
    [Windows::Foundation::Metadata::WebHostHidden]
    public ref class ForecastManager sealed : Windows::UI::Xaml::Data::INotifyPropertyChanged
    {
    public:
        virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler^ PropertyChanged;

        property Windows::Foundation::Collections::IObservableVector<ForecastGroup^>^ ForecastGroups
        {
            Windows::Foundation::Collections::IObservableVector<ForecastGroup^>^ get() { return m_forecastGroups; }
        }

        property Windows::Foundation::Collections::IObservableVector<Windows::Foundation::DateTime>^ Calendar
        {
            Windows::Foundation::Collections::IObservableVector<Windows::Foundation::DateTime>^ get() { return m_calendar; }
        }

        property Windows::Foundation::DateTime CurrentDate
        {
            Windows::Foundation::DateTime get() { return m_currentDate; }
            void set(Windows::Foundation::DateTime value)
            {
                if (m_currentDate.UniversalTime != value.UniversalTime)
                {
                    m_currentDate = value;
                    UpdateForecastDates(m_currentDate);
                    OnPropertyChanged("Calendar");
                }
            }
        }

        property Platform::String^ LastRefreshTime
        {
            Platform::String^ get();
        }

    internal:
        event ForecastAddedHandler^ ForecastAdded;

        static property ForecastManager^ Current
        {
        internal: ForecastManager^ get() { return m_current; }
        private: void set(ForecastManager^ value) { m_current = value; }
        }

        static property Platform::String^ DefaultHomeForecastUid
        {
            Platform::String^ get() { return "Redmond, WA, United States"; }
        }

        property Forecast^ HomeForecast
        {
            Forecast^ get() { return m_homeForecast; }
            void set(Forecast^ value)
            {
                if (value != m_homeForecast)
                {
                    if (m_homeForecast != nullptr)
                    {
                        m_homeForecast->IsHomeLocation = false;
                    }
                    m_homeForecast = value; OnPropertyChanged("HomeForecast");
                    if (m_homeForecast != nullptr)
                    {
                        m_homeForecast->IsHomeLocation = true;
                    }
                }
            }
        }

        property Forecast^ CurrentLocation
        {
            Forecast^ get() { return m_currentLocation; }
            void set(Forecast^ value)
            {
                if (value != m_currentLocation)
                {
                    m_currentLocation = value; OnPropertyChanged("CurrentLocation");
                }
            }
        }

        property Windows::Foundation::Collections::IObservableVector<Forecast^>^ AllForecastsSortedAlphabetically
        {
            Windows::Foundation::Collections::IObservableVector<Forecast^>^ get()
            {
                const std::vector<Forecast^> v = SortAlphabetically(GetAllForecasts());
                return ref new Platform::Collections::Vector<Forecast^>(begin(v), end(v));
            }
        }

        property Windows::Foundation::Collections::IObservableVector<Forecast^>^ VisibleForecastsSortedAlphabetically
        {
            Windows::Foundation::Collections::IObservableVector<Forecast^>^ get()
            {
                const std::vector<Forecast^> v = SortAlphabetically(GetVisibleForecasts());
                return ref new Platform::Collections::Vector<Forecast^>(begin(v), end(v));
            }
        }

        property Windows::Foundation::DateTime PreferredDate;

        concurrency::task<void> CreateThumbnailAsync(Forecast^ forecast);

        void LoadLocations(const web::json::value& v);

        Forecast^ AddForecast(const LatLon& location, Platform::String^ locality, Platform::String^ adminDistrict, Platform::String^ countryRegion);

        void RemoveForecast(Forecast^ forecast);

        concurrency::task<void> CheckinAsync(Forecast^ forecast, const utility::datetime& now);

        void UpdateCalendarRange(Windows::Foundation::DateTime minDate, Windows::Foundation::DateTime maxDate);

        void UpdateForecastDates(Windows::Foundation::DateTime date);

        inline bool PopThumbnailUpdateQueue(std::tuple<Forecast^, Windows::Storage::Streams::IRandomAccessStream^, concurrency::task_completion_event<void>> &result)
        {
            return m_thumbnailUpdateQueue.try_pop(result);
        }

        Forecast^ FindForecast(Platform::String^ uid);
        Forecast^ FindForecast(const LatLon& location, double toleranceFeet);

        std::vector<Forecast^> GetVisibleForecasts();

    private:
        friend ref class App;

        ForecastManager();

        void UpdateForecastDate(Forecast^ forecast, Windows::Foundation::DateTime date);
        std::vector<Forecast^> SortAlphabetically(const std::vector<Forecast^>& forecasts);
        std::vector<Forecast^> GetAllForecasts(); 

        struct equal_to : public std::binary_function<Windows::Foundation::DateTime, Windows::Foundation::DateTime, bool>
        {
            bool operator()(
                const Windows::Foundation::DateTime& _Left,
                const Windows::Foundation::DateTime& _Right
                ) const
            {
                return _Left.UniversalTime == _Right.UniversalTime;
            }
        };

        static ForecastManager^ m_current;
        std::shared_ptr<ExceptionPolicy> m_exceptionPolicy;
        Platform::Collections::Vector<ForecastGroup^>^ m_forecastGroups;
        std::unique_ptr<Web::IWeatherService> m_weatherService;
        Platform::Collections::Vector<Windows::Foundation::DateTime, equal_to>^ m_calendar;
        Windows::Foundation::DateTime m_currentDate;
        Windows::Foundation::DateTime m_minDate;
        Windows::Foundation::DateTime m_maxDate;
        concurrency::concurrent_queue<std::tuple<Forecast^, Windows::Storage::Streams::IRandomAccessStream^, concurrency::task_completion_event<void>>> m_thumbnailUpdateQueue;
        Forecast^ m_homeForecast;
        Forecast^ m_currentLocation;

        inline void OnPropertyChanged(Platform::String^ propertyName)
        {
            PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs(propertyName));
        }
    };
}
